"""
3.11 创建元组
本小节学习元组的概念和如何建立一个元组。Python 的元组与列表非常类似，它们的元
素都是用逗号分隔，元素类型任意；主要区别在于功能和形式上略有不同：功能上元组的直
接子元素不能修改，形式上元组使用小括号，而列表使用方括号。
3.11.1 直接创建元组
本知识点学习通过现有数据直接创建元组，创建时只需要在圆括号中添加元素，并使用
逗号隔开即可，语法形式如下：
元组变量名 = (元素 0, 元素 1, 元素 2……)
需要注意的是：元组的最后一个元素后面一般不加逗号（ , ），但如果元组只包含 1 个
元素，要在元素的后面加上逗号（ , ），否则不会被 Python 解释器认定为元组。
"""
t_tuple = ("张三","李四")
print(f't_tuple的类型为: {type(t_tuple)}')
t_tuple2 = ('张三')
print(f't_tuple2的类型为: {type(t_tuple2)}')
t_tuple3 = ('张三',)
print(f't_tuple3的类型为: {type(t_tuple3)}')
print('*' * 100)

"""
3.11.2 使用内置函数创建元组
本知识点学习通过 Python 语言内置函数 tuple()创建元组，tuple()函数可以将其他类型可
迭代对象（字符串、列表等）转换为元组，语法形式如下：
元组变量名 = tuple(可迭代对象)
"""
n_tuple = tuple([1,2,3,4])
print(f'n_tuple的内容为: {n_tuple}, 类型为: {type(n_tuple)}')
n_tuple2 = tuple('Python')
print(f'n_tuple2的内容为: {n_tuple2}, 类型为: {type(n_tuple2)}')
n_tuple3 = tuple('P')
print(f'n_tuple3的内容为: {n_tuple3}, 类型为: {type(n_tuple3)}')
n_tuple4 = tuple([1])
print(f'n_tuple4的内容为: {n_tuple4}, 类型为: {type(n_tuple4)}')
print('*' * 100)

"""
3.12 访问元组
元组的访问与列表类似，也通过索引访问内部元素。在 Python 语言中，元组是一种不
可变序列，可以查看内部元素的值但不可修改，因此元组的访问和使用相对列表更加简单。
3.12.1 使用索引访问元组
1. 元组长度
本知识点学习如何计算元组长度。如 3.2.1 小节所述，在 Python 语言中，计算元组长度
的方法与列表相同，语法格式如下：
长度 = len(元组变量名)
"""
my_tuple = tuple([1,2,3])
print(f'my_tuple的内容是: {my_tuple} 长度是: {len(my_tuple)}')
print('*' * 100)

"""
2. 元组的索引与不可修改性
本知识点学习元组的索引访问及其不可编辑性。元组的索引访问与列表完全相同，包括
正向索引和反向索引。与列表不同的是，元组是不可编辑的，只能通过索引访问元素值，不
能修改元素值。
【示例 3.12.1.2】通过索引访问元组元素，并在尝试修改元组元素值时被解释器抛出的
异常结束程序。
"""
student_tuple = tuple(["张三","Python",80])
print(f'张三上了: {student_tuple[1]} 课')
print(f'张三这门课的成绩是: {student_tuple[-1]}')
# 以下内容会报错，因为元组中的元素不可修改(注释掉的原因是因为防止代码报错)
# student_tuple[2] = 90
# print(f'张三这门课的成绩被修改为: {student_tuple[2]}')
print('*' * 100)


"""
3.12.2 通过元素值获得元组索引
1. 元组的“in/not in”运算符
与列表类似，元组也支持通过关键字“in”和“not in”测试某元素是否包含在列表中，相关
语法格式如下：
布尔值结果= 元素 in | not in 元组变量名
"""
student_tuple = tuple(["李四","Python",90])
print('李四' in student_tuple)
print(90 not in student_tuple)
print('*' * 100)


"""
2. 查找元组元素
如果要定位某元素在元组中的索引，可以使用元组内置方法 index()，语法格式如下：
元素索引 = 元组变量名.index(元素)
"""
student_tuple = tuple(["王五","Python",70])
print(student_tuple.index(70))
print("*" * 100)

"""
3.12.3 元组嵌套变量
本知识点学习元组元素嵌套变量的功能特性。因为变量具有可变性而元组具有不可变性，
因此当使用变量作为元素来创建元组时，实际上是使用变量的值作为元素来创建元组，而不
是将变量添加到元组中；元组创建后，修改变量不会导致元组发生改变。
【示例 3.12.3.1】通过变量 score 创建一个元组，通过修改变量 score 的值验证元组的值
不会同步修改。
"""
score = 75
student_tuple = tuple(["赵六", "Python", score])
print(f'赵六的成绩是: {student_tuple}')
score = 100
print(f'赵六的成绩是: {student_tuple}')
print("*" * 100)

"""
将此概念延伸至List列表
"""
score2 = 80
score_list = [score2]
print(f'score_list的内容是: {score_list}')
score2 = 90
print(f'score_list的内容是: {score_list}')
print("*" * 100)

"""
3.12.4 元组的切片
本知识点学习通过切片来批量获取元组中的部分元素。元组切片与列表切片方法完全相
同，可以从元组中连续或者固定间隔的取出部分元素，并将取出的元素组合成新元组返回。
语法格式如下：
新元组变量 = 原元组变量[ 头索引 : 尾索引 : 步长 ]
"""
n_tuple = tuple(range(0,8))
print(n_tuple[1:5:1]) #正序
print(n_tuple[5:1:-1])  #倒序
print("*" * 100)

"""
3.13 序列的封包与解包
本小节学习序列（列表和元组）的封包（Sequence Packing）和解包（Sequence Unpacking）
功能。序列封包是指程序把多个值赋给一个变量时，Python 会自动将多个值封装成元组。序
列解包是指程序允许将序列（字符串、元组、列表等）直接赋值给多个变量，此时序列的各
元素会被依次赋值给每个变量（要求序列的元素个数和变量个数相等）。
"""
"""
3.13.1 序列的封包
本知识点学习序列的封包方法，语法形式如下：
元组变量名 = 值 1, 值 2, 值 3, ……
需要注意的是：序列封包主要用于函数的多值返回，具体应用将在本书的函数部分讲解。
"""
"""
函数返回示例
"""
def func1():
    a = 1
    b = 2
    c = 3
    return a,b,c # 这里就涉及到了序列的封包
print(func1())
print("*" * 100)


"""
典型的例子(序列的封包)
"""
a = 1
b = 2
c = 3
d = a,b,c
print(f'd的内容为: {d}, d的类型为: {type(d)}')
print("*" * 100)


"""
3.13.2 序列的完全解包
本知识点学习序列的完全解包方法，所谓完全解包是指在解包过程中必须将序列中的所
有元素全部取出，否则程序会抛出 ValueError 异常而被迫强行停止。语法形式如下：
变量 1, 变量 2, …… , 变量 N = 序列变量名（元组或列表）
需要注意的是：完全解包的情况下，序列长度必须与赋值表达式左侧的变量数 N 相等；
序列中的元素与赋值表达式左边的变量按顺序一一对应进行赋值。
"""

"""
例子(序列的解包(完全解包))
元组
"""
student_tuple = tuple(['李四', '数学', 80])
name, course, score = student_tuple
print(f'name是{name}, course是{course}, score是{score}')
print("*" * 100)

"""
列表完全解包
"""
name_list = ['张三','李四','王五','赵六']
name1, name2, name3, name4 = name_list
print(f'{name1},{name2},{name3},{name4}')
print("*" * 100)

"""
【示例 3.13.2.3】利用序列的封包和解包功能，快速实现 2 个变量之间的数据交换。
"""
customer = 'RMB-Money'
salesPerson = 'Python Book'
print(f'交换前，顾客拥有：{customer}')
print(f'交换前，销售员拥有：{salesPerson}')
# 实现数据快速交换的实现方式
customer, salesPerson = salesPerson, customer
print(f'交换后，顾客拥有了：{customer}')
print(f'交换后，销售员拥有了：{salesPerson}')
print("*" * 100)


"""
3.13.3 序列的部分解包
本知识点学习序列的部分解包方法。所谓序列部分解包，是指序列解包时只解出部分元
素，未解出的元素使用列表变量保存。使用这种方式解包时，可以在左边被赋值的变量之前
添加“*”，这个变量就代表一个列表，可以保存多个元素。序列的部分解包有两种形式：
1. 前 m 个元素解包，后 N-m 个元素全部置入列表保存，语法形式为：
元素 1, 元素 2, *列表名 = 序列变量名（元组或列表）
2. 后 m 个元素解包，前 N-m 个元素全部置入列表保存，语法形式为：
*列表名, 元素 N-1, 元素 N= 序列变量名（元组或列表）
"""

"""
【示例 3.13.3.1】对列表前 1 个元素解包，后 2 个元素置入新列表保存。
"""
student_list = ['张三', "Python", 90]
# 正序的形式部分解包
name, *rest_list = student_list
print(f'姓名为: {name}, 类型为: {type(name)}')
print(f'其余的信息是: {rest_list}, 类型为: {type(rest_list)}')
print("*" * 100)

#倒叙的形式部分解包
*rest_list, score = student_list
print(f'score为: {score}, 类型为: {type(score)}')
print(f'其余的信息是: {rest_list}, 类型为: {type(rest_list)}')
print("*" * 100)

"""
3.14 ZIP 函数
3.14.1 序列压缩
本知识点学习 zip()函数的序列压缩功能，语法格式为：
zip 变量名 = zip(序列或字符串 1, 序列或字符串 2)
zip()函数将两个参数中对应位置的元素组装为元组，元组仅包含 2 个元素，第 0 个元素
来自序列或字符串 1，第 1 个元素来自序列或字符串 2 的相同位置，最后再将元组全部按顺
序放入一个 zip 对象。
如果要方便的使用 zip 对象，可以通过 list()函数将该 zip 对象转换为列表，语法格式为：
列表变量名 = list(zip 变量名)
需要注意的是：在压缩过程中，如果 zip()函数的两个序列或字符串参数的长度不一致，
会导致生成的元组内对应位置的元素缺失。
"""
"""
示例3.14.1.1 合并班级姓名及成绩
"""
name_list = ['贺峰', '王勃', '虞菊花', '王瑶', '龙霄汉', '王国伟']
score_list =  [81, 74, 92, 88, 80, 72]
zip_obj = zip(name_list, score_list)
print(zip_obj)
zip_list = list(zip_obj)
print(zip_list)

print("*" * 100)


"""
3.14.2 序列解压缩
本知识点学习序列的解压缩功能。序列的解压缩也通过 zip()函数实现，语法格式为：
元组变量 1, 元组变量 2 = zip(* 列表变量名)
需要注意的是：zip 参数内的列表变量，形式上应符合【示例 3.14.1.1】压缩生成的结果
列表，否则会出现 ValueError 异常。
"""
"""
【示例 3.14.2.1】将学生成绩列表解压缩为学生名单元组和成绩汇总元组。
"""
student_list = [('贺峰', 81), ('王勃', 74), ('虞菊花', 92), ('王瑶', 88)]
name_tuple, score_tuple = zip(* student_list)
print(name_tuple)
print(score_tuple)







